<?php
declare(strict_types=1);

namespace src;

use ArrayObject;
use JsonSerializable;
use src\interfaces\IResponse;
use src\response\JsonResponse;

class Router
{
    protected ?Container $container;

    protected ?Request $currentRequest;

    protected RouteCollection $routeCollection;

    public function __construct(Container $container = null)
    {
        $this->container = $container;
        $this->routeCollection = new RouteCollection();
    }

    /**
     * @param Request $request
     * @return Response
     */
    public function dispatch(Request $request)
    {
        $this->currentRequest = $request;
        return $this->dispatchToRoute($request);
    }

    /**
     * @param Request $request
     * @return Response void
     */
    public function dispatchToRoute(Request $request)
    {
        return $this->runRoute($request, $this->findRoute($request));
    }

    protected function findRoute($request)
    {
        $route = $this->routeCollection->match($request);
        if (empty($route)){
            throw new \Exception('没有找到路由');
        }
        $route->setContainer($this->container);
        $this->container->instance(Route::class, $route);
        return $route;
    }

    protected function runRoute($request, Route $route)
    {
        $middleware = [];
        return (new Pipeline($this->container))
            ->send($request)
            ->through($middleware)
            ->then(function ($request) use ($route) {
                return $this->prepareResponse($request, $route->run());
            });
    }

    public function prepareResponse($request, $response)
    {
        if ($response instanceof IResponse){
            $response = $response->toResponse($request);
        }elseif ($response instanceof ArrayObject || $response instanceof  JsonSerializable) {
            $response = new JsonResponse($response);
        }else{
            $response = new Response($response, "200");
        }
        return $response;
    }

    public static function toResponse($request, $response)
    {
        if ($response instanceof Response) {
            $response = $response->toResponse($request);
        }
    }

    public function get($uri, $action = null)
    {
        return $this->addRoute(['GET', 'HEAD'], $uri, $action);
    }

    public function post($uri, $action = null)
    {
        return $this->addRoute(['POST'], $uri, $action);
    }

    public function addRoute($methods, $uri, $action)
    {
        return $this->routeCollection->add($this->createRoute($methods, $uri, $action));
    }

    public function createRoute($methods, $uri, $action)
    {
        return (new Route($methods, $uri, $action))
            ->setRouter($this);
    }
}